# v1.4.0

# 功能测试

## http 健康检查日志

mosn配置文件 `./mosn_config.json`

```json
{
	"servers":[
		{
			"default_log_path":"stdout",
			"listeners":[
				{
					"name":"appListener",
					"address": "127.0.0.1:2047",
					"bind_port": true,
					"filter_chains": [{
						"filters": [
							{
								"type": "proxy",
								"config": {
									"downstream_protocol": "Http1",
									"cluster": "serverCluster"
								}
							}
						]
					}]
				}
			]
		}
	],
	"cluster_manager":{
		"clusters":[
			{
				"name":"serverCluster",
				"type": "SIMPLE",
				"lb_type": "LB_RANDOM",
                "health_check": {
                    "protocol": "Http1",
                    "service_name": "service8080",
                    "event_log_path": "stdout"
                },
				"hosts":[
					{"address":"127.0.0.1:8080"}
				]
			}
		]
	},
	"admin": {
		"address": {
			"socket_address": {
				"address": "0.0.0.0",
				"port_value": 34901
			}
		}
	}
}
```

启动 mosn 查看输出日志

```bash
$ go run ./mosn start -c ./mosn_config.json
...
2023-01-06 14:43:32,167 [INFO] [admin store] [start service] start service Mosn Admin Server on [::]:34901
2023-01-06 14:43:33,167 [INFO] [server] [reconfigure] reconfigureHandler start
time:1672987413,host:127.0.0.1:8080,health_status:1,current_result:1,status_changed:0
time:1672987428,host:127.0.0.1:8080,health_status:1,current_result:1,status_changed:0
time:1672987443,host:127.0.0.1:8080,health_status:1,current_result:1,status_changed:0
```

## 最小连接数负载均衡器

mosn配置文件 `./mosn_config.json`

```json
{
	"servers":[
		{
			"default_log_path":"stdout",
			"listeners":[
				{
					"name":"appListener",
					"address": "127.0.0.1:2047",
					"bind_port": true,
					"filter_chains": [{
						"filters": [
							{
								"type": "tcp_proxy",
								"config": {
									"cluster": "serverCluster"
								}
							}
						]
					}]
				}
			]
		}
	],
	"cluster_manager":{
		"clusters":[
			{
				"name":"serverCluster",
				"type": "SIMPLE",
				"lb_type": "LB_LEAST_CONNECTION",
				"hosts":[
					{"address":"127.0.0.1:9090"},
					{"address":"127.0.0.1:9091"},
					{"address":"127.0.0.1:9092"}
				]
			}
		]
	},
	"admin": {
		"address": {
			"socket_address": {
				"address": "0.0.0.0",
				"port_value": 34901
			}
		}
	}
}
```

go 服务端代码 `./server.go`
```go
package main

import (
	"fmt"
	"net"
	"os"
	"time"
)

var addr = "0.0.0.0:8080"
var delay = false

func main() {
	avgs := os.Args

	for i, arg := range avgs {
		switch i {
		case 0:
			continue
		case 1:
			addr = arg
		case 2:
			if arg == "delay" {
				delay = true
			}
		}
	}

	listen, err := net.Listen("tcp", addr)
	if err != nil {
		fmt.Println(err)
		return
	}

	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println(err)
			continue
		}
		go handle(conn)
	}
}

func handle(conn net.Conn) {
	defer conn.Close()
	if delay {
		time.Sleep(2 * time.Second)
	}
	conn.Write([]byte(addr))
}
```

go 客户端代码 `./client.go`
```go
package main

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

type Count struct {
	m     *sync.Mutex
	count map[string]int
}

var c Count
var num int64 = 0

func main() {

	addr := "127.0.0.1:8090"
	sum := 1000
	c = Count{
		m:     &sync.Mutex{},
		count: map[string]int{},
	}

	args := os.Args
	for i, arg := range args {
		switch i {
		case 0:
			continue
		case 1:
			addr = arg
		case 2:
			sum, _ = strconv.Atoi(arg)
		}
	}

	c = Count{
		m:     &sync.Mutex{},
		count: map[string]int{},
	}

	for i := 0; i < sum; {
		if num < 100 {
			go dial(addr)
			atomic.AddInt64(&num, 1)
			i++
		}
	}
	time.Sleep(4 * time.Second)
	for remote, sum := range c.count {
		fmt.Printf("%s 服务器连接了 %d 次\n", remote, sum)
	}
}

func dial(addr string) {
	defer atomic.AddInt64(&num, -1)

	d := net.Dialer{}
	conn, err := d.Dial("tcp", addr)
	if err != nil {
		fmt.Println(err)
	}

	defer conn.Close()
	response := make([]byte, 1024)
	conn.Read(response)
	c.incr(string(response))
}

func (c Count) incr(remote string) {
	c.m.Lock()
	defer c.m.Unlock()
	if _, exist := c.count[remote]; exist {
		c.count[remote] += 1
	} else {
		c.count[remote] = 1
	}
}
```

使用 go 服务端模拟 3 个上游服务(和 mosn 配置的 hosts 对应)，上游返回自身监听的 addr，启动时令一个服务器延时回复以保持 tcp 连接
使用 go 客户端访问 mosn 代理，并统计访问各个上游的次数，可以观察到访问延时回复的服务器次数最少

```Bash
$ ./mosn start -c ./mosn_config.json
$ go run server.go 127.0.0.1:9090 delay
$ go run server.go 127.0.0.1:9091
$ go run server.go 127.0.0.1:9092
$ go run client.go http://127.0.0.1:2047

127.0.0.1:9091 服务器连接了 2250 次
127.0.0.1:9092 服务器连接了 2175 次
127.0.0.1:9090 服务器连接了 575 次
```

## 平滑升级失败时，新 mosn 不再删除 reconfig.sock

bug 描述：平滑升级过程中出现错，新的 mosn 退出时会删除 reconfig.sock，使后续无法再执行平滑升级操作。

首先运行一个 hack mosn，hack mosn 在触发升级后不发送消息。随后分别验证 mosn1.3.0 和 mosn1.4.0 在升级失败时的行为.

目录结构
```Bash
$ ls
hack_mosn               mosn1.3.0               mosn1.4.0               mosn_config.json
```

启动 hack_mosn
```Bash
$ ./hack_mosn start -c ./mosn_config.json 
$ ls
hack_mosn               mosn1.3.0               mosn1.4.0               mosn_config.json        reconfig.sock
```

启动 mosn1.3.0
```Bash
$ ./mosn1.3.0 start -c ./mosn_config.json 
...
2023-01-06 19:17:45,31 [ERROR] [mosn] [NewMosn] getInheritListeners failed, exit
...
2023-01-06 19:17:45,31 [ERROR] [start] failed to start application at stage: 2
2023-01-06 19:17:45,31 [INFO] [stagemanager] state changed to 11

$ ls
hack_mosn               mosn1.3.0               mosn1.4.0               mosn_config.json
```

重启 hack_mosn，并启动 mosn1.4.0
```Bash
$ ./mosn1.3.0 start -c ./mosn_config.json 
...
2023-01-06 19:21:33,143 [ERROR] [mosn] [NewMosn] getInheritListeners failed, exit
...
2023-01-06 19:21:33,143 [ERROR] [start] failed to start application at stage: 2
2023-01-06 19:21:33,143 [INFO] [stagemanager] state changed to 11

$ ls
hack_mosn               mosn1.3.0               mosn1.4.0               mosn_config.json        reconfig.sock
```

# 基准测试

+ 使用[sofaload](https://github.com/antJack/sofaload)在本地搭建简单的性能测试

## v1.4.0测试结果

```Bash
sofaload -D 10 --qps=2000 -c 200 -t 16 -p sofarpc sofarpc://127.0.0.1:12200
starting benchmark...
Application protocol: sofarpc

finished in 10.00s, 1999.90 req/s, 2.41MB/s
requests: 20000 total, 20000 started, 19999 done, 19999 succeeded, 0 failed, 0 errored, 0 timeout
sofaRPC status codes: 
	19999 success, 0 error, 0 server exception, 0 unknown
	0 server threadpool busy, 0 error comm, 0 no processor, 0 timeout
	0 client send error, 0 codec exception, 0 connection closed, 0 server serial exception
	0 server deserial exception
traffic: 24.11MB (25278736) total, 390.61KB (399980) headers (space savings 0.00%), 23.73MB (24878756) data
                     min         max         mean         sd        +/- sd
time for request:      131us     12.69ms       282us       364us    97.42%
time for connect:        4us        38us        15us         7us    68.00%
req/s           :       9.50       10.60       10.00        0.41    79.00%

  Latency  Distribution
   50%        217us
   75%        293us
   90%        418us
   95%        523us
   99%       1.09ms
```

## v1.3.0测试结果

```Bash
sofaload -D 10 --qps=2000 -c 200 -t 16 -p sofarpc sofarpc://127.0.0.1:12200
starting benchmark...
Application protocol: sofarpc

finished in 10.00s, 2000.00 req/s, 2.41MB/s
requests: 20000 total, 20000 started, 20000 done, 20000 succeeded, 0 failed, 0 errored, 0 timeout
sofaRPC status codes: 
	20000 success, 0 error, 0 server exception, 0 unknown
	0 server threadpool busy, 0 error comm, 0 no processor, 0 timeout
	0 client send error, 0 codec exception, 0 connection closed, 0 server serial exception
	0 server deserial exception
traffic: 24.11MB (25280000) total, 390.63KB (400000) headers (space savings 0.00%), 23.73MB (24880000) data
                     min         max         mean         sd        +/- sd
time for request:      122us      8.74ms       282us       334us    97.47%
time for connect:        4us       123us        20us        25us    93.50%
req/s           :       9.50       10.60       10.00        0.41    76.50%

  Latency  Distribution
   50%        219us
   75%        297us
   90%        410us
   95%        510us
   99%       1.30ms
```
